Building Custom Modals, Dialogs, and Bottom Sheets in React Native
Course: React Native Training
Topic: Building Custom Modals, Dialogs, and Bottom Sheets
Course Link: React Native Training at JustAcademy
Demo Registration: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to Modals, Dialogs, and Bottom Sheets
Modals, dialogs, and bottom sheets are interactive interface elements used to display information, collect user input, request confirmation, and provide additional actions without navigating away from the current screen.
In React Native applications, these elements are commonly used in login forms, confirmation messages, shopping carts, filters, profile editing, payment flows, and settings screens. They help organize the interface by displaying additional content only when it is needed.
Although these elements may look similar, they have different purposes and interaction patterns.
1.1 What Is a Modal?
A modal is a layer that appears above the current screen and temporarily directs the user's attention to a specific task. It may cover the entire screen or display a smaller panel over a dimmed background.
Examples include:
- Opening a login or registration form.
- Displaying product details.
- Showing a full-screen image preview.
- Presenting a multi-step form.
- Displaying an important notice.
1.2 What Is a Dialog?
A dialog is a focused message or interaction panel that asks the user to read information, make a decision, or confirm an action.
Examples include:
- Confirming whether to delete an account.
- Asking whether unsaved changes should be discarded.
- Confirming an order cancellation.
- Displaying an error message with an action to retry.
1.3 What Is a Bottom Sheet?
A bottom sheet is a panel that slides upward from the bottom edge of the screen. It may occupy a small portion of the screen or expand to display more content.
Examples include:
- Displaying sorting and filtering options.
- Showing sharing actions.
- Presenting product variations.
- Displaying location or map details.
- Showing a list of actions for a selected item.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand the differences between modals, dialogs, and bottom sheets.
- Use React Native's built-in
Modal component.
- Create custom overlays with reusable React Native components.
- Implement confirmation dialogs and handle user responses.
- Build bottom sheets with interactive content.
- Control visibility using React state.
- Add animations, backdrops, and dismissal behavior.
- Manage keyboard interaction, safe areas, and accessibility.
- Use a third-party bottom sheet library when advanced gestures are required.
- Test overlays on Android and iOS.
3. Modal vs. Dialog vs. Bottom Sheet
| Feature |
Modal |
Dialog |
Bottom Sheet |
| Purpose |
Displays additional content above the current screen |
Focuses attention on a message or decision |
Displays contextual content and actions from the bottom |
| Typical position |
Centered, full-screen, or another configured presentation |
Usually centered or positioned as a compact panel |
Attached to the bottom edge |
| Content size |
Small to full-screen |
Usually compact |
Partial-height or expandable |
| Interaction |
Forms, details, workflows, and custom content |
Confirmation and short decisions |
Filtering, actions, selections, and contextual details |
| Dismissal |
Close button, action, or supported gestures |
Action buttons or supported dismissal |
Close button, backdrop, or swipe gesture when enabled |
| Example |
Login form |
Delete confirmation |
Product filter panel |
A dialog can technically be implemented inside a modal, and a bottom sheet can also be presented as a modal. These terms describe different aspects of an interface rather than completely separate technologies.
4. Understanding React Native's Modal Component
React Native provides a built-in Modal component for presenting content above the enclosing view. Its visibility is controlled with the visible property.
Official documentation: React Native Modal.
4.1 Important Modal Properties
| Property |
Purpose |
visible |
Controls whether the modal is displayed. |
animationType |
Controls the presentation animation: none, slide, or fade. |
transparent |
Allows the underlying screen to show through the modal window. |
onRequestClose |
Handles Android hardware-back dismissal and relevant platform close requests. |
onShow |
Runs when the modal has been shown. |
onDismiss |
Runs after dismissal on supported platforms. |
presentationStyle |
Controls supported iOS modal presentation styles. |
statusBarTranslucent |
Controls whether the Android modal can extend beneath the status bar. |
5. Building a Basic Custom Modal
The first step is to create a modal whose visibility can be controlled by a button. The modal can contain a title, description, and close action.
5.1 Complete Example
import React, { useState } from 'react';
import {
View,
Text,
Modal,
Pressable,
StyleSheet
} from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={styles.screen}>
<Text style={styles.heading}>Custom Modal Example</Text>
<Pressable
style={styles.openButton}
onPress={() => setModalVisible(true)}
accessibilityRole="button"
>
<Text style={styles.buttonText}>Open Modal</Text>
</Pressable>
<Modal
visible={modalVisible}
transparent
animationType="fade"
onRequestClose={() => setModalVisible(false)}
>
<View style={styles.overlay}>
<View style={styles.modalCard}>
<Text style={styles.title}>Welcome</Text>
<Text style={styles.message}>
This is a custom modal in React Native.
</Text>
<Pressable
style={styles.closeButton}
onPress={() => setModalVisible(false)}
accessibilityRole="button"
accessibilityLabel="Close modal"
>
<Text style={styles.buttonText}>Close</Text>
</Pressable>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#F3F4F6'
},
heading: {
fontSize: 23,
fontWeight: 'bold',
marginBottom: 20
},
openButton: {
backgroundColor: '#2563EB',
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 8
},
buttonText: {
color: '#FFFFFF',
fontWeight: '600'
},
overlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
modalCard: {
width: '100%',
maxWidth: 400,
backgroundColor: '#FFFFFF',
padding: 24,
borderRadius: 16,
alignItems: 'center'
},
title: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 12,
color: '#111827'
},
message: {
fontSize: 16,
lineHeight: 24,
textAlign: 'center',
color: '#4B5563',
marginBottom: 20
},
closeButton: {
backgroundColor: '#2563EB',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8
}
});
5.2 How the Code Works
useState(false) initializes the modal as hidden.
- The Open Modal button updates the state to
true.
- The
visible property displays the modal when the state is true.
transparent allows a custom overlay and modal card to be rendered.
- The overlay darkens the background behind the modal.
- The Close button sets visibility to false.
onRequestClose handles the Android hardware-back request.
animationType="fade" applies a fade presentation animation.
6. Customizing the Modal Appearance
A custom modal should match the application's design system. Developers can control its width, background, border radius, spacing, typography, shadows, and action buttons.
6.1 Important Design Properties
- Width: Use a responsive width and a reasonable maximum width.
- Padding: Provide enough space around the content.
- Border radius: Use consistent rounded corners.
- Backdrop: Use a suitable transparent overlay to separate the modal from the screen.
- Typography: Use a clear title and readable body text.
- Actions: Make primary and secondary actions visually distinct.
- Elevation: Use appropriate shadow or separation where supported.
6.2 Making the Modal Responsive
A modal may look good on one phone but exceed the available height on another device. For longer content, use a scrollable container and respect safe areas.
import {
Modal,
View,
ScrollView,
Text
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export function DetailsModal({ visible, onClose }) {
return (
<Modal
visible={visible}
animationType="slide"
onRequestClose={onClose}
>
<SafeAreaView style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ padding: 20 }}>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
Details
</Text>
<Text style={{ marginTop: 12, lineHeight: 24 }}>
Place longer modal content here. The content can
scroll when it exceeds the available screen height.
</Text>
</ScrollView>
</SafeAreaView>
</Modal>
);
}
This example uses SafeAreaView from react-native-safe-area-context. Install and configure that package according to its documentation if it is not already present in the project.
7. Building a Custom Confirmation Dialog
Confirmation dialogs are useful when an action may remove information, cancel an operation, or cause a significant change. The dialog should clearly explain the action and provide an explicit way to confirm or cancel.
7.1 Example: Delete Confirmation
import React, { useState } from 'react';
import {
View,
Text,
Modal,
Pressable,
StyleSheet
} from 'react-native';
export default function DeleteExample() {
const [dialogVisible, setDialogVisible] = useState(false);
const [status, setStatus] = useState('');
const confirmDelete = () => {
setDialogVisible(false);
setStatus('Delete action confirmed.');
};
const cancelDelete = () => {
setDialogVisible(false);
setStatus('Delete action cancelled.');
};
return (
<View style={styles.screen}>
<Pressable
style={styles.deleteButton}
onPress={() => setDialogVisible(true)}
>
<Text style={styles.buttonText}>Delete Item</Text>
</Pressable>
{status ? <Text style={styles.status}>{status}</Text> : null}
<Modal
visible={dialogVisible}
transparent
animationType="fade"
onRequestClose={cancelDelete}
>
<View style={styles.overlay}>
<View
style={styles.dialog}
accessibilityRole="alert"
>
<Text style={styles.title}>Delete Item?</Text>
<Text style={styles.message}>
This action may permanently remove the selected item.
Do you want to continue?
</Text>
<View style={styles.actions}>
<Pressable
style={styles.cancelButton}
onPress={cancelDelete}
accessibilityRole="button"
>
<Text>Cancel</Text>
</Pressable>
<Pressable
style={styles.confirmButton}
onPress={confirmDelete}
accessibilityRole="button"
>
<Text style={styles.buttonText}>Delete</Text>
</Pressable>
</View>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20
},
deleteButton: {
backgroundColor: '#B91C1C',
padding: 14,
borderRadius: 8
},
buttonText: {
color: '#FFFFFF',
fontWeight: '600'
},
overlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
dialog: {
width: '100%',
maxWidth: 400,
padding: 22,
backgroundColor: '#FFFFFF',
borderRadius: 14
},
title: {
fontSize: 21,
fontWeight: 'bold',
color: '#111827'
},
message: {
marginTop: 12,
lineHeight: 22,
color: '#4B5563'
},
actions: {
flexDirection: 'row',
justifyContent: 'flex-end',
marginTop: 24,
gap: 12
},
cancelButton: {
padding: 12,
borderRadius: 8,
backgroundColor: '#E5E7EB'
},
confirmButton: {
padding: 12,
borderRadius: 8,
backgroundColor: '#B91C1C'
},
status: {
marginTop: 16
}
});
7.2 Important Implementation Details
- Cancel closes the dialog without confirming the action.
- Delete represents the confirmation path; in a real application, connect it to the actual deletion operation.
- The message clearly explains the consequence of the action.
- The destructive action uses a different visual style from Cancel.
- The Android back request closes the dialog through the same cancellation handler.
8. Creating a Reusable Dialog Component
Instead of rebuilding a confirmation dialog on every screen, create a reusable component with configurable properties. This reduces duplication and keeps dialog behavior consistent.
8.1 Reusable Dialog Example
import React from 'react';
import {
Modal,
View,
Text,
Pressable,
StyleSheet
} from 'react-native';
type ConfirmDialogProps = {
visible: boolean;
title: string;
message: string;
confirmLabel?: string;
cancelLabel?: string;
onConfirm: () => void;
onCancel: () => void;
};
export default function ConfirmDialog({
visible,
title,
message,
confirmLabel = 'Confirm',
cancelLabel = 'Cancel',
onConfirm,
onCancel
}: ConfirmDialogProps) {
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onCancel}
>
<View style={styles.overlay}>
<View style={styles.dialog}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.message}>{message}</Text>
<View style={styles.actions}>
<Pressable
onPress={onCancel}
accessibilityRole="button"
style={styles.cancel}
>
<Text>{cancelLabel}</Text>
</Pressable>
<Pressable
onPress={onConfirm}
accessibilityRole="button"
style={styles.confirm}
>
<Text style={{ color: '#FFFFFF' }}>
{confirmLabel}
</Text>
</Pressable>
</View>
</View>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: 'rgba(0,0,0,0.5)'
},
dialog: {
width: '100%',
maxWidth: 400,
backgroundColor: '#FFFFFF',
padding: 22,
borderRadius: 14
},
title: {
fontSize: 20,
fontWeight: 'bold'
},
message: {
marginTop: 10,
lineHeight: 22,
color: '#4B5563'
},
actions: {
flexDirection: 'row',
justifyContent: 'flex-end',
gap: 12,
marginTop: 22
},
cancel: {
padding: 12,
backgroundColor: '#E5E7EB',
borderRadius: 8
},
confirm: {
padding: 12,
backgroundColor: '#2563EB',
borderRadius: 8
}
});
8.2 Using the Reusable Dialog
<ConfirmDialog
visible={showDialog}
title="Discard Changes?"
message="Your unsaved changes will be lost."
confirmLabel="Discard"
cancelLabel="Keep Editing"
onConfirm={discardChanges}
onCancel={() => setShowDialog(false)}
/>
In a production implementation, ensure that the component is placed inside the appropriate screen or application tree and that the state and callback functions are defined by its parent.
9. Handling Backdrop Presses
A backdrop is the dimmed area behind a modal or dialog. Some interfaces allow users to tap the backdrop to dismiss the overlay. This behavior is useful for non-destructive panels, but it may be inappropriate for forms or important confirmations where accidental dismissal could cause confusion.
9.1 Implementing a Dismissible Backdrop
One way to implement a custom overlay is to use a full-screen Pressable as the backdrop and place the dialog above it. Ensure that pressing inside the dialog does not accidentally trigger the backdrop's close action.
<View style={styles.overlay}>
<Pressable
style={StyleSheet.absoluteFill}
onPress={onClose}
accessibilityRole="button"
accessibilityLabel="Dismiss dialog"
/>
<View style={styles.dialog}>
<Text>Dialog content</Text>
</View>
</View>
For production code, test touch handling carefully and consider whether a backdrop dismissal should be available at all. A visible close button should be provided when appropriate.
10. Building a Bottom Sheet from Scratch
A simple bottom sheet can be built using React Native's Modal, a transparent backdrop, and a panel aligned to the bottom of the screen. This approach is suitable for basic interfaces, although it does not automatically provide advanced dragging, snapping, or gesture coordination.
10.1 Basic Bottom Sheet Example
import React, { useState } from 'react';
import {
View,
Text,
Modal,
Pressable,
StyleSheet
} from 'react-native';
export default function BottomSheetExample() {
const [visible, setVisible] = useState(false);
return (
<View style={styles.screen}>
<Pressable
style={styles.openButton}
onPress={() => setVisible(true)}
accessibilityRole="button"
>
<Text style={styles.buttonText}>Open Bottom Sheet</Text>
</Pressable>
<Modal
visible={visible}
transparent
animationType="slide"
onRequestClose={() => setVisible(false)}
>
<View style={styles.modalRoot}>
<Pressable
style={styles.backdrop}
onPress={() => setVisible(false)}
accessibilityRole="button"
accessibilityLabel="Close bottom sheet"
/>
<View style={styles.sheet}>
<View style={styles.handle} />
<Text style={styles.title}>
Choose an Action
</Text>
<Pressable style={styles.option} onPress={() => setVisible(false)}>
<Text>View Profile</Text>
</Pressable>
<Pressable style={styles.option} onPress={() => setVisible(false)}>
<Text>Share Item</Text>
</Pressable>
<Pressable style={styles.option} onPress={() => setVisible(false)}>
<Text>Report Item</Text>
</Pressable>
<Pressable
style={styles.cancelButton}
onPress={() => setVisible(false)}
>
<Text style={styles.buttonText}>Close</Text>
</Pressable>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
openButton: {
backgroundColor: '#2563EB',
padding: 14,
borderRadius: 8
},
buttonText: {
color: '#FFFFFF',
fontWeight: '600'
},
modalRoot: {
flex: 1,
justifyContent: 'flex-end'
},
backdrop: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0,0,0,0.45)'
},
sheet: {
backgroundColor: '#FFFFFF',
padding: 24,
paddingBottom: 32,
borderTopLeftRadius: 22,
borderTopRightRadius: 22
},
handle: {
width: 44,
height: 5,
borderRadius: 4,
backgroundColor: '#D1D5DB',
alignSelf: 'center',
marginBottom: 20
},
title: {
fontSize: 21,
fontWeight: 'bold',
marginBottom: 16
},
option: {
paddingVertical: 15,
borderBottomWidth: 1,
borderBottomColor: '#E5E7EB'
},
cancelButton: {
marginTop: 18,
alignItems: 'center',
backgroundColor: '#2563EB',
padding: 13,
borderRadius: 8
}
});
10.2 Understanding the Bottom Sheet
modalRoot fills the modal window and aligns the sheet to the bottom.
backdrop dims the rest of the screen.
sheet contains the bottom sheet's content.
handle visually suggests that the panel can be dragged.
animationType="slide" animates the modal window.
- Option buttons perform their actions and close the sheet in this example.
Important: The handle in this example is visual only. This implementation does not support dragging the sheet by the handle. Use a gesture-enabled bottom sheet library if users need to expand, collapse, or dismiss the sheet by swiping.
11. Using a Bottom Sheet Library
For more advanced bottom sheets, a dedicated library can provide snapping points, swipe gestures, backdrop behavior, scrollable content, keyboard handling, and smoother interactions.
One popular option is React Native Bottom Sheet. Its modal API provides methods such as present() and dismiss(), and the library supports configurable sheet behavior. Read the Bottom Sheet Modal documentation.
11.1 Installation
Install the package and its required dependencies according to the official instructions for your React Native or Expo setup.
npm install @gorhom/bottom-sheet
For Expo projects, the documented Expo installation flow commonly uses:
npx expo install react-native-reanimated react-native-gesture-handler
Check the current library documentation for version compatibility, Reanimated configuration, and the required root view setup. The exact installation steps depend on the project's framework and versions.
11.2 Example with BottomSheetModal
import React, { useCallback, useRef } from 'react';
import {
View,
Text,
Button,
StyleSheet
} from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
BottomSheetModal,
BottomSheetModalProvider,
BottomSheetView
} from '@gorhom/bottom-sheet';
export default function App() {
const bottomSheetRef = useRef<BottomSheetModal>(null);
const openSheet = useCallback(() => {
bottomSheetRef.current?.present();
}, []);
const closeSheet = useCallback(() => {
bottomSheetRef.current?.dismiss();
}, []);
return (
<GestureHandlerRootView style={styles.container}>
<BottomSheetModalProvider>
<View style={styles.screen}>
<Button
title="Open Bottom Sheet"
onPress={openSheet}
/>
</View>
<BottomSheetModal
ref={bottomSheetRef}
snapPoints={['40%', '75%']}
>
<BottomSheetView style={styles.content}>
<Text style={styles.title}>
Bottom Sheet Content
</Text>
<Text style={styles.description}>
This sheet can expand between configured snap points.
</Text>
<Button title="Close" onPress={closeSheet} />
</BottomSheetView>
</BottomSheetModal>
</BottomSheetModalProvider>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1
},
screen: {
flex: 1,
justifyContent: 'center',
padding: 24
},
content: {
flex: 1,
padding: 24
},
title: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 12
},
description: {
marginBottom: 20,
lineHeight: 22
}
});
11.3 Understanding the Library Example
BottomSheetModalProvider supplies the modal context required by the library.
BottomSheetModal represents the bottom sheet modal.
useRef stores a reference to the modal instance.
present() opens the modal.
dismiss() dismisses the modal.
snapPoints defines the sheet's configured resting heights.
BottomSheetView holds the sheet's content.
Follow the library's current documentation for exact setup and configuration requirements. Do not mix APIs from different major versions without checking compatibility.
12. Adding Animations to Custom Modals
Animations help users understand when an overlay appears or disappears. React Native's built-in Modal supports the none, slide, and fade animation types.
12.1 Available Modal Animations
| Animation |
Behavior |
Suitable Use |
none |
Displays without a built-in entrance animation |
Simple or immediate presentations |
fade |
Fades the modal into view |
Confirmation dialogs and compact overlays |
slide |
Slides the modal into view |
Panels and full-screen flows |
12.2 Creating Custom Animation
For more control over a modal card's opacity or movement, developers can use React Native's Animated API. A custom animation requires appropriate state and animation lifecycle handling. For interactive dragging and advanced gesture coordination, a gesture and animation library may be more suitable.
Reference: React Native Animated API.
13. Handling Keyboard Interaction
Modals often contain text fields, such as login inputs, search fields, or forms. When the keyboard opens, it may cover part of the modal. Use keyboard-aware layouts and test the behavior on both Android and iOS.
13.1 Using KeyboardAvoidingView
import React from 'react';
import {
Modal,
View,
TextInput,
Button,
KeyboardAvoidingView,
Platform
} from 'react-native';
export function LoginModal({ visible, onClose }) {
return (
<Modal
visible={visible}
animationType="slide"
onRequestClose={onClose}
>
<KeyboardAvoidingView
style={{ flex: 1, justifyContent: 'center', padding: 24 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<View>
<TextInput
placeholder="Email address"
keyboardType="email-address"
autoCapitalize="none"
style={{
borderWidth: 1,
borderColor: '#9CA3AF',
padding: 12,
marginBottom: 12,
borderRadius: 8
}}
/>
<TextInput
placeholder="Password"
secureTextEntry
style={{
borderWidth: 1,
borderColor: '#9CA3AF',
padding: 12,
marginBottom: 16,
borderRadius: 8
}}
/>
<Button title="Close" onPress={onClose} />
</View>
</KeyboardAvoidingView>
</Modal>
);
}
13.2 Keyboard Best Practices
- Ensure the focused field remains visible when the keyboard opens.
- Use suitable keyboard types for email addresses, numbers, and other inputs.
- Provide a clear action to submit or dismiss the keyboard.
- Test long forms on smaller devices.
- Check the behavior with platform-specific keyboard settings.
14. Accessibility Considerations
Overlays can disrupt navigation for users who rely on screen readers, keyboard navigation, or other assistive technologies. Accessibility should be part of the design and implementation process.
14.1 Accessibility Checklist
- Provide a meaningful title and clear description.
- Use appropriate accessibility roles for buttons and interactive controls.
- Give icon-only controls accessible labels.
- Ensure text and controls have sufficient contrast.
- Provide an obvious close or cancel action where appropriate.
- Make the Android back-button behavior predictable.
- Ensure the dialog's content is accessible without requiring precise gestures.
- Prevent background interaction when the overlay is intended to be modal.
- Test screen-reader focus and announcements on both platforms.
15. Modal and Bottom Sheet Design Best Practices
- Use overlays purposefully: Avoid showing modals for information that could be displayed directly on the screen.
- Keep dialog messages concise: Clearly explain the decision or action required.
- Use meaningful actions: Prefer labels such as Save, Cancel, Delete, or Continue instead of vague labels.
- Prevent accidental data loss: Confirm destructive actions and warn about unsaved changes.
- Make content responsive: Support small screens, larger text, and different orientations where applicable.
- Manage dismissal: Decide whether tapping the backdrop or pressing the Android back button should close the overlay.
- Handle asynchronous actions: Prevent repeated submissions while saving or processing data.
- Use bottom sheets for contextual actions: They work well for filters, sharing, and item-specific options.
- Keep animations purposeful: Use animations that clarify transitions without delaying the user.
- Test on real devices: Check gestures, keyboard behavior, safe areas, and platform-specific differences.
16. Common Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Modal does not appear |
The visibility state is false or the component is not mounted |
Check the state and verify the component is rendered in the expected tree. |
| Modal cannot be closed with Android back |
onRequestClose is missing or does not update visibility |
Implement onRequestClose and update the relevant state. |
| Backdrop does not cover the screen |
Incorrect flex layout or overlay positioning |
Use a full-screen container and check the overlay styles. |
| Backdrop tap closes the panel unexpectedly |
Touch events are reaching the backdrop from the panel |
Review the view hierarchy and touch handling. |
| Dialog is too wide |
Fixed width exceeds the available screen space |
Use a responsive width and a suitable maximum width. |
| Modal content is clipped |
Content is taller than the available window |
Use a scrollable container and account for safe areas. |
| Keyboard covers input fields |
Keyboard-aware layout is missing or unsuitable |
Test KeyboardAvoidingView and adjust the layout for the platform. |
| Bottom sheet cannot be dragged |
A basic modal panel has no gesture implementation |
Use a gesture-enabled bottom sheet library. |
| Bottom sheet gestures conflict with scrolling |
Gesture and scrollable configuration is incorrect |
Follow the library's guidance for integrated scrollables and gestures. |
| Content appears beneath system bars |
Safe areas or modal presentation settings are incorrect |
Check safe-area handling and platform-specific modal properties. |
| Multiple dialogs appear at once |
Several visibility states are active simultaneously |
Coordinate overlay state and define which overlays can coexist. |
17. Mini Project: Product Details and Actions
Project objective: Create a shopping application screen where users can open product details in a modal, confirm removal of an item with a dialog, and open a bottom sheet for sorting or filtering.
17.1 Requirements
- Display a product card with a name and price.
- Provide a button to open product details.
- Display a confirmation dialog before removing a product.
- Provide a bottom sheet with sorting options.
- Include close and cancel actions.
- Use consistent styling for buttons, panels, and text.
- Handle Android back-button behavior.
- Ensure content remains accessible on smaller screens.
17.2 Suggested Folder Structure
ShoppingApp/
├── App.tsx
└── src/
├── components/
│ ├── ProductCard.tsx
│ ├── ProductDetailsModal.tsx
│ ├── ConfirmDialog.tsx
│ └── SortBottomSheet.tsx
├── screens/
│ └── ProductScreen.tsx
└── data/
└── products.ts
17.3 Implementation Steps
- Create the product listing screen.
- Build a reusable product card.
- Implement a modal for displaying product details.
- Create a reusable confirmation dialog.
- Build a bottom sheet for sorting options.
- Manage visibility and selection state.
- Connect buttons to the relevant actions.
- Add backdrop and close behavior where appropriate.
- Test on Android and iOS.
- Verify accessibility, scrolling, and keyboard behavior.
18. Practice Exercises
- Create a modal that displays a welcome message.
- Build a confirmation dialog for deleting a task.
- Create a reusable dialog component with configurable title and actions.
- Implement a backdrop that dismisses a non-destructive modal.
- Build a bottom sheet containing three action options.
- Extend the bottom sheet using a gesture-enabled library.
- Create a login modal with email and password fields.
- Prevent duplicate form submissions while a request is in progress.
- Test Android hardware-back behavior.
- Improve accessibility by adding appropriate labels and roles.
19. Interview Questions and Answers
Q1. What is a modal in React Native?
Answer: A modal presents content above the current screen and temporarily focuses the user on a task or additional information.
Q2. What is the React Native Modal component?
Answer: It is a built-in component used to display content in a separate modal presentation above the enclosing view.
Q3. What is the difference between a modal and a dialog?
Answer: A modal is a presentation mechanism for additional content, while a dialog is generally a focused message or decision interface that may be implemented using a modal.
Q4. What is a bottom sheet?
Answer: A bottom sheet is a panel that appears from the bottom of the screen and presents contextual information or actions.
Q5. What does the visible property do?
Answer: It controls whether a React Native Modal is displayed.
Q6. What is the purpose of onRequestClose?
Answer: It handles close requests such as the Android hardware-back button for a modal.
Q7. How can a modal be animated?
Answer: The built-in Modal supports none, fade, and slide animation types. Custom animations can be implemented using suitable animation APIs.
Q8. How do you make a reusable dialog?
Answer: Create a component with properties for visibility, title, message, action labels, and callback functions. This allows multiple screens to reuse the same dialog.
Q9. How can a basic bottom sheet be implemented?
Answer: Use a transparent modal, a backdrop, and a panel aligned to the bottom of the screen. Add a dedicated gesture library if dragging and snapping are required.
Q10. Why use a bottom sheet library?
Answer: A library can provide gesture handling, snapping points, animations, scroll integration, keyboard support, and configurable dismissal behavior.
Q11. How should a modal handle keyboard input?
Answer: Use suitable keyboard-aware layouts, keep focused inputs visible, and test on different devices and operating systems.
Q12. What accessibility considerations apply to dialogs?
Answer: Provide meaningful labels, readable text, appropriate accessibility roles, predictable focus and dismissal behavior, and sufficient contrast.
20. Quick Revision Checklist
- Understand the differences between modals, dialogs, and bottom sheets.
- Use React Native's built-in
Modal component.
- Control modal visibility with state.
- Understand
visible, transparent, animationType, and onRequestClose.
- Create reusable confirmation dialogs.
- Manage backdrop presses carefully.
- Understand that a basic bottom sheet does not automatically support dragging.
- Use a dedicated library for advanced bottom sheet gestures.
- Handle keyboard behavior and safe areas.
- Test accessibility, dismissal, and platform-specific behavior.
21. Official Learning Resources
22. Conclusion
Custom modals, dialogs, and bottom sheets help React Native developers create focused and interactive mobile experiences. The built-in Modal component is suitable for many overlay requirements, while reusable dialog components make confirmations and alerts consistent across an application.
Basic bottom sheets can be built with a modal and a bottom-aligned panel. When advanced dragging, snapping, and scroll coordination are required, a dedicated library such as React Native Bottom Sheet can provide those features. Good implementations also account for responsive layouts, keyboard behavior, accessibility, platform-specific dismissal, and performance.
To continue learning, explore the React Native Training course at JustAcademy or register for a course demo.